package com.zm.admin.aop;


import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.zm.admin.annotation.SysLogTag;
import com.zm.admin.utils.ResultBean;
import com.zm.admin.utils.ServletUtil;
import com.zm.admin.utils.UserIpUtil;
import com.zm.admin.zmeng.domain.ZmRole;
import com.zm.admin.zmeng.domain.ZmSysLog;
import com.zm.admin.zmeng.domain.ZmUser;
import com.zm.admin.zmeng.mapper.ZmRoleMapper;
import com.zm.admin.zmeng.mapper.ZmSysLogMapper;
import com.zm.admin.zmeng.mapper.ZmUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/***
 *
 * @Auther: MuYun
 * @Date: 2022/12/13 21:28
 * @Description： 日志切面类
 *
 */
@Aspect
@Slf4j
@Component
public class SysLogAop {

    @Resource
    private ZmRoleMapper zmRoleMapper;

    @Resource
    private ZmUserMapper zmUserMapper;

    @Resource
    private ZmSysLogMapper zmSysLogMapper;

    ZmSysLog sysLog=null;

    /**
     * 切点
     */
    //@Pointcut("execution(* com.zm.admin.zmeng.controller.*.*(..))")//指定包下所有方法
    @Pointcut("@annotation(com.zm.admin.annotation.SysLogTag)")//所有使用@SysLogTag注解的方法
    private void controller(){
    }

    /**
     * 前置通知
     * @param point
     */
    @Before("controller()")
    private void before(JoinPoint point){
        HttpServletRequest request = ServletUtil.getRequest();
        try{
            SysLogTag logTag = getTag(point);
            if(ObjectUtil.isNotNull(logTag)){
                //log.info("前置");
                sysLog=new ZmSysLog();
                sysLog.setIp(UserIpUtil.getIp(request));
                sysLog.setMethod(ServletUtil.getMethod());
                sysLog.setParams(ServletUtil.getQueryParam());
                sysLog.setCreateTime(DateUtil.date());
                //log.info("注解desc参数:{}",logTag);
                sysLog.setDescription(logTag.desc());
                sysLog.setLogType(logTag.type());
            }

        }catch (Exception e){
            log.error("日志切面前置报错:{}",e);
        }
    }

    /**
     * 后置通知
     * @param point
     */
    @After("controller()")
    private void after(JoinPoint point){
        try{
            SysLogTag logTag = getTag(point);
            if(StpUtil.isLogin()&&ObjectUtil.isNotNull(logTag)){
                //log.info("后置");
                String loginId = StpUtil.getLoginId().toString();
                ZmRole zmRole = zmRoleMapper.selectUserRole(loginId);
                ZmUser zmUser = zmUserMapper.selectById(loginId);
                sysLog.setUserType(zmRole.getRoleName());
                sysLog.setUsername(zmUser.getUsername());
                log.info("看看待入库的日志：{}",sysLog);
                zmSysLogMapper.insert(sysLog);
            }else{
                //log.error("looklook日志切面后置通知的时候有没有登陆上,{},该方法是否使用注解:{}",StpUtil.isLogin(),ObjectUtil.isNotNull(logTag));
            }
        }catch (Exception e){
            log.error("日志切面后置报错:{}",e);
        }
    }

    /**
     * 后置增强，当切面方法正常返回时执行
     * @param point
     * @param returnValue
     */
    @AfterReturning(value = "controller()",returning = "returnValue")
    private void afterReturn(JoinPoint point,Object returnValue){
        try{
            SysLogTag logTag = getTag(point);

            if(ObjectUtil.isNotNull(logTag)&&ObjectUtil.isNotNull(sysLog)){
                //log.info("后置返回");
                ResultBean resultBean= (ResultBean) returnValue;
                if(ObjectUtil.isEmpty(resultBean)){
                    sysLog.setResultCode("200");
                    sysLog.setResult("该接口没有返回结果（基本是正常执行状态");
                }else {
                    sysLog.setResultCode(JSON.toJSONString(resultBean.getCode()));
                    sysLog.setResult(JSON.toJSONString(resultBean.setCode(resultBean.getCode()).setMsg(resultBean.getMsg())));
                }

            }
        }catch (Exception e){
            log.error("日志切面后置return报错:{}",e);
        }
    }

    /**
     * 抛出异常增强：当切面方法抛出异常时执行
     * @param point
     * @param e
     */
    @AfterThrowing(value = "controller()",throwing = "e")
    private void afterThrow(JoinPoint point,Throwable e){
        try{
            SysLogTag logTag = getTag(point);
            if(ObjectUtil.isNotNull(logTag)&&ObjectUtil.isNotNull(sysLog)){
                //log.info("后置抛异常");
                sysLog.setResultCode("500");
                sysLog.setResult(JSON.toJSONString(ResultBean.getError(e.getMessage())));
                sysLog.setExceptionDetail(e.getMessage());
            }
        }catch (Exception exception){
            log.error("日志切面后置throw报错:{}",exception);
        }
    }

    private static SysLogTag getTag(JoinPoint point){
        //获取方法上注解的参数内容
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        SysLogTag logTag = method.getAnnotation(SysLogTag.class);
        return logTag;
    }

}
